<template>
  <div class="container">
    <canvas canvas-id="canvas" 
    @touchstart="touchstart" 
    @touchmove="touchmove" 
    @touchend="touchend"
    :style="{width: width+'px',height: height+'px'}"
    @error="canvasIdErrorCallback"/>
    <button @click="draw">绘制</button>
    <button @click="clear">清除</button>
  </div>
</template>

<script>
/* eslint-disable no-unused-vars */
/* eslint-disable standard/no-callback-literal */
let lastFrameTime = 0;
// 模拟 requestAnimationFrame
let requestAnimationFrame = function(callback) {
    let currTime = new Date().getTime();
    let timeToCall = Math.max(0, 16 - (currTime - lastFrameTime));
    let id = setTimeout(
        function() {
            callback(currTime + timeToCall);
        }, timeToCall);
    lastFrameTime = currTime + timeToCall;
    return id;
};
// // 模拟 cancelAnimationFrame
// let abortAnimationFrame = function(id) {
//     clearTimeout(id);
// };

let canvas = {
    width: 300,
    height: 500
};
function factorial(num) { // 递归阶乘
    if (num <= 1) {
        return 1;
    } else {
        return num * factorial(num - 1);
    }
}

function bezier(arr, t) { // 通过各控制点与占比t计算当前贝塞尔曲线上的点坐标
    let x = 0;
    let y = 0;
    let n = arr.length - 1;
    arr.forEach(function(item, index) {
        if (!index) {
            x += item.x * Math.pow((1 - t), n - index) * Math.pow(t, index);
            y += item.y * Math.pow((1 - t), n - index) * Math.pow(t, index);
        } else {
            x += factorial(n) / factorial(index) / factorial(n - index) * item.x * Math.pow((1 - t), n - index) * Math.pow(t, index);
            y += factorial(n) / factorial(index) / factorial(n - index) * item.y * Math.pow((1 - t), n - index) * Math.pow(t, index);
        }
    });
    return {
        x: x,
        y: y
    };
}
function drawBezier(ctx, originNodes) {
    if (t > 1) {
        isPrinting = false;
        return;
    }
    isPrinting = true;
    let nodes = originNodes;
    t += 0.01;
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    drawnode(nodes);
    requestAnimationFrame(drawBezier.bind(this, ctx, nodes));
}
function drawnode(nodes) {
    if (!nodes.length) { return; }
    let _nodes = nodes;
    let nextNodes = [];
    let startX = 0;
    let startY = 0;
    _nodes.forEach(function(item, index) {
        let x = item.x;
        let y = item.y;
        if (_nodes.length === num) {
            ctx.font = '16px Microsoft YaHei';
            let i = parseInt(index, 10) + 1;
            ctx.fillText('p' + i, x, y + 20);
            ctx.fillText('p' + i + ': (' + x + ', ' + y + ')', 10, i * 20);
        }
        ctx.beginPath();
        ctx.arc(x, y, 4, 0, Math.PI * 2, false);
        ctx.fill();
        if (_nodes.length === 1) {
            bezierNodes.push(item);
            if (bezierNodes.length > 1) {
                bezierNodes.forEach(function(obj, i) {
                    if (i) {
                        let startX = bezierNodes[i - 1].x;
                        let startY = bezierNodes[i - 1].y;
                        let x = obj.x;
                        let y = obj.y;
                        ctx.beginPath();
                        ctx.moveTo(startX, startY);
                        ctx.lineTo(x, y);
                        ctx.strokeStyle = 'red';
                        ctx.stroke();
                    }
                });
            }
        }
        if (index) {
            startX = _nodes[index - 1].x;
            startY = _nodes[index - 1].y;
            ctx.beginPath();
            ctx.moveTo(startX, startY);
            ctx.lineTo(x, y);
            ctx.strokeStyle = '#696969';
            ctx.stroke();
        }
        ctx.draw(true);
    });
    if (_nodes.length) {
        for (let i = 0; i < _nodes.length - 1; i++) {
            let arr = [{
                x: _nodes[i].x,
                y: _nodes[i].y
            }, {
                x: _nodes[i + 1].x,
                y: _nodes[i + 1].y
            }];
            nextNodes.push(bezier(arr, t));
        }
        drawnode(nextNodes);
    }
}

let t = 0; // 贝塞尔函数涉及的占比比例，0<=t<=1
let ctx = null;
let clickNodes = []; // 点击的控制点对象数组
let bezierNodes = []; // 绘制内部控制点的数组
let isPrinted = false; // 当前存在绘制的曲线
let isPrinting = false; // 正在绘制中
let num = 0; // 控制点数
let isDrag = false; // 是否进入拖拽行为
let isDragNode = false; // 是否点击到了控制点
let dragIndex = 0; // 被拖拽的点的索引
let clickon = 0; // 鼠标按下时间戳
let clickoff = 0; // 鼠标抬起
let touchstartX = 0;
let touchstartY = 0;
export default {
    data() {
        return {
            width: 0,
            height: 0
        };
    },
    methods: {
        touchstart(e) {
            let target = e.target;
            isDrag = true;
            clickon = new Date().getTime();
            let diffLeft = target.offsetLeft;
            let diffTop = target.offsetTop;
            touchstartX = e.x;
            touchstartY = e.y;
            let x = touchstartX - diffLeft;
            let y = touchstartY - diffTop;
            clickNodes.forEach(function(item, index) {
                let absX = Math.abs(item.x - x);
                let absY = Math.abs(item.y - y);
                if (absX < 5 && absY < 5) {
                    isDragNode = true;
                    dragIndex = index;
                }
            });
        },
        touchmove(e) {
            let target = e.target;
            let startX = 0;
            let startY = 0;
            if (isDrag && isDragNode) {
                let diffLeft = target.offsetLeft;
                let diffTop = target.offsetTop;
                let clientX = e.x;
                let clientY = e.y;
                let x = clientX - diffLeft;
                let y = clientY - diffTop;
                clickNodes[dragIndex] = {
                    x: x,
                    y: y
                };
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                clickNodes.forEach(function(item, index) {
                    let x = item.x;
                    let y = item.y;
                    let i = parseInt(index, 10) + 1;
                    ctx.fillText('p' + i, x, y + 20);
                    ctx.fillText('p' + i + ': (' + x + ', ' + y + ')', 10, i * 20);
                    ctx.beginPath();
                    ctx.arc(x, y, 4, 0, Math.PI * 2, false);
                    ctx.fill();
                    ctx.beginPath();
                    ctx.moveTo(startX, startY);
                    ctx.lineTo(x, y);
                    ctx.strokeStyle = '#696969';
                    ctx.stroke();
                    if (index) {
                        startX = clickNodes[index - 1].x;
                        startY = clickNodes[index - 1].y;
                        ctx.beginPath();
                        ctx.moveTo(startX, startY);
                        ctx.lineTo(x, y);
                        ctx.stroke();
                    }
                });
                if (isPrinted) {
                    let bezierArr = [];
                    for (let i = 0; i < 1; i += 0.01) {
                        bezierArr.push(bezier(clickNodes, i));
                    }
                    bezierArr.forEach(function(obj, index) {
                        if (index) {
                            startX = bezierArr[index - 1].x;
                            startY = bezierArr[index - 1].y;
                            x = obj.x;
                            y = obj.y;
                            ctx.beginPath();
                            ctx.moveTo(startX, startY);
                            ctx.lineTo(x, y);
                            ctx.strokeStyle = 'red';
                            ctx.stroke();
                        }
                    });
                }
                ctx.draw(false);
            }
        },
        touchend(e) {
            isDrag = false;
            isDragNode = false;
            clickoff = new Date().getTime();
            let target = e.target;
            let startX = 0;
            let startY = 0;
            if (clickoff - clickon < 200) {
                let diffLeft = target.offsetLeft;
                let diffTop = target.offsetTop;
                let clientX = touchstartX;
                let clientY = touchstartY;
                let x = clientX - diffLeft;
                let y = clientY - diffTop;
                if (!isPrinted && !isDragNode) {
                    num++;
                    ctx.font = '16px Microsoft YaHei';
                    ctx.fillStyle = 'black';
                    ctx.fillText('p' + num, x, y + 20);
                    ctx.fillText('p' + num + ': (' + x + ', ' + y + ')', 10, num * 20);
                    ctx.beginPath();
                    ctx.arc(x, y, 4, 0, Math.PI * 2, false);
                    ctx.fill();
                    if (clickNodes.length) {
                        startX = clickNodes[clickNodes.length - 1].x;
                        startY = clickNodes[clickNodes.length - 1].y;
                        ctx.beginPath();
                        ctx.moveTo(startX, startY);
                        ctx.lineTo(x, y);
                        ctx.strokeStyle = '#696969';
                        ctx.stroke();
                    }
                    ctx.draw(true);
                    clickNodes.push({
                        x: x,
                        y: y
                    });
                }
            }
        },
        draw() {
            if (!num) {
                return;
            }
            if (!isPrinting) {
                isPrinted = true;
                drawBezier(ctx, clickNodes);
            }
        },
        clear() {
            if (!isPrinting) {
                isPrinted = false;
                ctx.clearRect(0, 0, canvas.width, canvas.height);
                ctx.draw();
                clickNodes = [];
                bezierNodes = [];
                t = 0;
                num = 0;
            }
        },
        canvasIdErrorCallback: function(e) {
            console.error(e.detail.errMsg);
        }
    },
    onReady() {
        wx.getSystemInfo({
            // 获取系统信息成功，将系统窗口的宽高赋给页面的宽高
            success: res => {
                this.width = canvas.width = res.windowWidth;
                // console.log(that.width)   375
                this.height = canvas.width = res.windowHeight - 100;
                // console.log(that.height)  625
                // 这里的单位是PX，实际的手机屏幕有一个Dpr，这里选择iphone，默认Dpr是2
            }
        });
        ctx = wx.createCanvasContext('canvas');
    }
};
</script>

<style lang="scss" scoped>
.container{
  height: 80vh;
  width: 100vw;
  canvas {
  }
}
</style>